// 22_file_watcher.rs
// Rust 文件监听器示例

//! 本教程展示如何使用 Rust 实现一个文件系统监听器，用于监控目录和文件的变化。
//! 主要内容包括：
//! 1. 监听目录和文件的新增、修改、删除操作
//! 2. 使用 notify crate 实现跨平台文件监听
//! 3. 记录文件变化事件到日志文件
//! 4. 异步处理文件事件
//! 5. 优雅地处理错误和关闭监听器

use notify::{Event, EventKind, RecursiveMode, Watcher};
use notify_debouncer_full::{new_debouncer, DebounceEventResult, Debouncer, FileIdMap};
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;
use tokio::time::timeout;

/// 文件监听器结构体
pub struct FileWatcher {
    debouncer: Debouncer<notify::RecommendedWatcher, FileIdMap>,
    log_file: File,
    rx: Receiver<DebounceEventResult>,
}

impl FileWatcher {
    /// 创建新的文件监听器
    pub fn new(log_path: &str) -> Result<Self, Box<dyn std::error::Error>> {
        // 创建或打开日志文件
        let log_file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(log_path)?;

        // 创建通道用于接收文件事件
        let (tx, rx) = channel();

        // 创建防抖动监听器（合并短时间内多次触发的事件）
        let debouncer = new_debouncer(
            Duration::from_millis(500), // 防抖动时间
            None,
            move |res: DebounceEventResult| {
                tx.send(res).unwrap();
            },
        )?;

        Ok(FileWatcher {
            debouncer,
            log_file,
            rx,
        })
    }

    /// 添加要监听的路径
    pub fn watch_path(&mut self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
        self.debouncer
            .watcher()
            .watch(Path::new(path), RecursiveMode::Recursive)?;
        self.log_event(&format!("开始监听路径: {}", path));
        Ok(())
    }

    /// 停止监听指定路径
    pub fn unwatch_path(&mut self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
        self.debouncer
            .watcher()
            .unwatch(Path::new(path))?;
        self.log_event(&format!("停止监听路径: {}", path));
        Ok(())
    }

    /// 记录事件到日志文件
    fn log_event(&mut self, message: &str) {
        let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
        let log_entry = format!("[{}] {}\n", timestamp, message);
        
        if let Err(e) = self.log_file.write_all(log_entry.as_bytes()) {
            eprintln!("写入日志文件失败: {}", e);
        }
        
        if let Err(e) = self.log_file.flush() {
            eprintln!("刷新日志文件失败: {}", e);
        }
        
        // 同时在控制台输出
        println!("[{}] {}", timestamp, message);
    }

    /// 处理文件事件
    fn handle_event(&mut self, event: Event) {
        let path_str: String = event.paths
            .iter()
            .map(|p| p.to_string_lossy().to_string())
            .collect::<Vec<_>>()
            .join(", ");

        match event.kind {
            EventKind::Create(_) => {
                self.log_event(&format!("文件创建: {}", path_str));
            }
            EventKind::Modify(_) => {
                self.log_event(&format!("文件修改: {}", path_str));
            }
            EventKind::Remove(_) => {
                self.log_event(&format!("文件删除: {}", path_str));
            }
            EventKind::Other => {
                self.log_event(&format!("其他事件: {}", path_str));
            }
            _ => {
                self.log_event(&format!("未知事件: {:?}", event));
            }
        }
    }

    /// 运行监听器
    pub async fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        self.log_event("文件监听器启动");
        
        loop {
            // 使用 tokio 的 timeout 来定期检查是否需要退出
            match timeout(Duration::from_secs(1), async {
                match self.rx.recv() {
                    Ok(result) => match result {
                        Ok(events) => {
                            for event in events {
                                self.handle_event(event.event);
                            }
                        }
                        Err(errors) => {
                            for error in errors {
                                self.log_event(&format!("监听错误: {}", error));
                            }
                        }
                    },
                    Err(e) => {
                        self.log_event(&format!("接收事件错误: {}", e));
                    }
                }
            }).await {
                Ok(_) => {},
                Err(_) => {
                    // 超时，可以在这里处理其他逻辑或检查退出条件
                }
            }
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== Rust 文件监听器示例 ===\n");
    
    // 创建文件监听器实例
    let mut watcher = FileWatcher::new("file_watcher.log")?;
    
    // 添加要监听的路径
    // 注意：你需要根据实际情况修改这些路径
    let current_dir = std::env::current_dir()?;
    println!("当前工作目录: {}", current_dir.display());
    
    // 监听当前目录
    watcher.watch_path(".")?;
    
    // 如果你有特定的目录要监听，可以添加它们
    // watcher.watch_path("/path/to/your/directory")?;
    
    println!("\n文件监听器已启动，正在监听文件变化...");
    println!("请在被监听的目录中创建、修改或删除文件来测试监听器");
    println!("按 Ctrl+C 停止监听器\n");
    
    // 运行监听器
    // 使用 tokio::select! 来同时处理监听器和退出信号
    tokio::select! {
        _ = watcher.run() => {
            println!("监听器已停止");
        }
        _ = tokio::signal::ctrl_c() => {
            println!("\n接收到 Ctrl+C，正在停止监听器...");
        }
    }
    
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use std::thread;
    use std::time::Duration;
    
    #[test]
    fn test_file_watcher_creation() {
        let result = FileWatcher::new("test_watcher.log");
        assert!(result.is_ok());
        
        // 清理测试文件
        let _ = fs::remove_file("test_watcher.log");
    }
    
    #[tokio::test]
    async fn test_file_watcher_watch_path() {
        let mut watcher = FileWatcher::new("test_watcher.log").unwrap();
        let result = watcher.watch_path(".");
        assert!(result.is_ok());
        
        // 清理测试文件
        let _ = fs::remove_file("test_watcher.log");
    }
    
    #[tokio::test]
    async fn test_file_operations_simulation() {
        // 创建测试目录
        let test_dir = "test_watch_dir";
        let _ = fs::create_dir(test_dir);
        
        // 创建文件监听器
        let mut watcher = FileWatcher::new("test_operations.log").unwrap();
        watcher.watch_path(test_dir).unwrap();
        
        // 在单独的线程中运行监听器一小段时间
        let handle = std::thread::spawn(move || {
            thread::sleep(Duration::from_millis(100));
        });
        
        // 模拟文件操作
        thread::sleep(Duration::from_millis(100));
        let test_file = format!("{}/test.txt", test_dir);
        fs::write(&test_file, "Hello, World!").unwrap();
        thread::sleep(Duration::from_millis(100));
        fs::write(&test_file, "Modified content").unwrap();
        thread::sleep(Duration::from_millis(100));
        let _ = fs::remove_file(&test_file);
        thread::sleep(Duration::from_millis(100));
        
        // 清理
        let _ = fs::remove_dir_all(test_dir);
        let _ = fs::remove_file("test_operations.log");
        handle.join().unwrap();
    }
}


